using System.Reflection;
using System.Text.RegularExpressions;
using Aurora.TaskScheduler.Abstractions;
using Aurora.TaskScheduler.Abstractions.BaseModel;
using Aurora.TaskScheduler.Abstractions.Message;
using Aurora.TaskScheduler.Abstractions.SchedulerAttributes;
using Aurora.TaskScheduler.Extension.BaseJobs;
using Aurora.TaskScheduler.Extension.BaseServices;
using Aurora.TaskScheduler.Extension.Internel;
using Aurora.TaskScheduler.Extension.SchedulerImpl;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using Quartz.Impl;
using Quartz.Spi;

namespace Aurora.TaskScheduler.Extension;

public static class TaskSchedulerExtension
{
    public static void AddTaskScheduler(this IServiceCollection services, Action<TaskSchedulerOptions> setupAction)
    {
        if (setupAction == null) throw new ArgumentNullException(nameof(setupAction));
        services.AddScoped<HttpResultJob>();
        services.AddScoped<ClassLibraryJob>();
        services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
        services.AddSingleton<IJobFactory, ASPDIJobFactory>();
        services.AddScoped<TaskJobUtil>();
        services.AddScoped<ITaskHandler, QuartzTaskHandler>();
        services.AddHttpContextAccessor();
        services.AddHttpClient();
        var options = new TaskSchedulerOptions();
        setupAction(options);

        foreach (ITaskScheduleOptionsExtension extension in options.Extensions)
        {
            extension.AddServices(services);
        }

        services.Configure(setupAction);

        InitializeTask(services, options);
    }


    /// <summary>
    /// 初始化任务
    /// </summary>
    /// <param name="services"></param>
    /// <param name="options"></param>
    private static void InitializeTask(IServiceCollection services, TaskSchedulerOptions options)
    {
        var jobInterfaceType = typeof(ITaskJob<>);
        var handler = services.BuildServiceProvider().GetService<ITaskHandler>();
        var assemblies = GetReferenceAssemblies(options.Assemblies.ToArray());

        if (options.RetentionTime != -1)
        {
            services.AddScoped(typeof(ITaskJob<string>), typeof(TaskRemoveHandler));
            var attribute = typeof(TaskRemoveHandler).GetCustomAttribute<CronAttribute>();
            var task = new AppTask()
            {
                TaskName = string.IsNullOrEmpty(attribute!.TaskName) ? nameof(TaskRemoveHandler) : attribute.TaskName,
                GroupName = attribute.GroupName ?? options.GetGroupName(),
                DllClassName = nameof(TaskRemoveHandler),
                TaskType = 1,
                ApiParameter = string.Empty,
                Interval = attribute.Cron,
                Status = (int)JobState.开启,
                IntervalDescription = attribute.CronName,
                ArgumentTypeName = typeof(string).ToString(),
                Describe = attribute.Description
            };
            ArgumentTypeExtension.Add(nameof(TaskRemoveHandler), typeof(string));
            handler?.AddJobNoStart(task);
        }

        List<AppTask> tasks = new List<AppTask>();
        foreach (Assembly assembly in assemblies)
        {
            var concretions = new List<Type>();
            try
            {
                var enumerable = assembly.GetTypes().Where(t => !t.IsOpenGenericType());
                foreach (Type item in enumerable)
                {
                    // 查找接口
                    var interfaceTypes = item.FindInterfacesThatClose(jobInterfaceType).ToArray();
                    if (!interfaceTypes.Any())
                    {
                        continue;
                    }

                    if (item.IsConcrete())
                    {
                        if (item == typeof(TaskRemoveHandler))
                        {
                            continue;
                        }

                        concretions.Add(item);
                    }
                }

                if (!concretions.Any())
                    continue;

                foreach (Type item in concretions)
                {
                    var makeGenericType = item.FindInterfacesThatClose(jobInterfaceType).FirstOrDefault();
                    if (makeGenericType == null)
                        continue;
                    var genericArgument = makeGenericType.GetGenericArguments()[0];
                    //
                    var genericType = jobInterfaceType.MakeGenericType(genericArgument);
                    services.AddTransient(genericType, item);
                    var attribute = item.GetCustomAttribute<CronAttribute>();
                    if (attribute == null)
                        continue;
                    if (string.IsNullOrEmpty(attribute.Cron))
                    {
                        throw new ArgumentInvalidException($"{item.Name} cron is empty");
                    }

                    if (!MatchCronRegex(attribute.Cron))
                    {
                        throw new ArgumentInvalidException($"{item.Name} cron is invalid");
                    }

                    var task = new AppTask()
                    {
                        TaskName = string.IsNullOrEmpty(attribute.TaskName) ? item.Name : attribute.TaskName,
                        GroupName = attribute.GroupName ?? options.GetGroupName(),
                        DllClassName = item.Name,
                        TaskType = 1,
                        ApiParameter = string.Empty,
                        Interval = attribute.Cron,
                        Status = (int)JobState.开启,
                        IntervalDescription = attribute.CronName,
                        Describe = attribute.Description,
                        ArgumentTypeName = genericArgument.ToString()
                    };
                    ArgumentTypeExtension.Add(item.Name, genericArgument);
                    tasks.Add(task);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }

        if (tasks.Count() > 0)
        {
            handler?.AddJobNoStart(tasks);
        }
    }


    /// <summary>
    /// 匹配cron表达式
    /// </summary>
    /// <param name="cron"></param>
    /// <returns></returns>
    private static bool MatchCronRegex(string cron)
    {
      return  CronExpression.IsValidExpression(cron);
        // Regex regex = new Regex(
        //     "^\\s*($|#|\\w+\\s*=|(\\?|\\*|(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?(?:,(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?)*)\\s+(\\?|\\*|(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?(?:,(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?)*)\\s+(\\?|\\*|(?:[01]?\\d|2[0-3])(?:(?:-|\\/|\\,)(?:[01]?\\d|2[0-3]))?(?:,(?:[01]?\\d|2[0-3])(?:(?:-|\\/|\\,)(?:[01]?\\d|2[0-3]))?)*)\\s+(\\?|\\*|(?:0?[1-9]|[12]\\d|3[01])(?:(?:-|\\/|\\,)(?:0?[1-9]|[12]\\d|3[01]))?(?:,(?:0?[1-9]|[12]\\d|3[01])(?:(?:-|\\/|\\,)(?:0?[1-9]|[12]\\d|3[01]))?)*)\\s+(\\?|\\*|(?:[1-9]|1[012])(?:(?:-|\\/|\\,)(?:[1-9]|1[012]))?(?:L|W)?(?:,(?:[1-9]|1[012])(?:(?:-|\\/|\\,)(?:[1-9]|1[012]))?(?:L|W)?)*|\\?|\\*|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?(?:,(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)*)\\s+(\\?|\\*|(?:[0-6])(?:(?:-|\\/|\\,|#)(?:[0-6]))?(?:L)?(?:,(?:[0-6])(?:(?:-|\\/|\\,|#)(?:[0-6]))?(?:L)?)*|\\?|\\*|(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?(?:,(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?)*)(|\\s)+(\\?|\\*|(?:|\\d{4})(?:(?:-|\\/|\\,)(?:|\\d{4}))?(?:,(?:|\\d{4})(?:(?:-|\\/|\\,)(?:|\\d{4}))?)*))$");
        // if (string.IsNullOrEmpty(cron))
        //     return false;
        // var isMatch = regex.IsMatch(cron);
        // return isMatch;
    }

    /// <summary>
    /// 获取程序集
    /// </summary>
    /// <returns></returns>
    private static List<Assembly> GetReferenceAssemblies(params Assembly[] extendAssemblies)
    {
        List<Assembly> result = new List<Assembly>();
        var assemblies = AppDomain.CurrentDomain.GetAssemblies();
        foreach (Assembly assembly in assemblies)
        {
            result.Add(assembly);
        }

        if (extendAssemblies?.Length > 0)
        {
            result.AddRange(extendAssemblies);
        }

        return result;
    }

    /// <summary>
    /// 开启任务执行
    /// </summary>
    /// <param name="app"></param>
    public static void UseTaskScheduler(this IApplicationBuilder app)
    {
        IServiceProvider services = app.ApplicationServices;
        using var serviceScope = services.CreateScope();
        var dd = serviceScope.ServiceProvider.GetService<ITaskHandler>();
        dd?.InitJobs();
    }

    /// <summary>
    /// 开启任务执行
    /// </summary>
    /// <param name="app"></param>
    /// <param name="action"></param>
    public static void UseTaskScheduler(this IApplicationBuilder app, Action<IApplicationBuilder> action)
    {
        action(app);
        app.UseTaskScheduler();
    }


    #region private method

    /// <summary>
    /// 是否是泛型类型
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private static bool IsOpenGenericType(this Type type)
    {
        return type.IsGenericTypeDefinition || type.ContainsGenericParameters;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="pluggedType"></param>
    /// <param name="templateType"></param>
    /// <returns></returns>
    internal static IEnumerable<Type> FindInterfacesThatClose(this Type pluggedType, Type templateType)
    {
        return FindInterfacesThatClosesCore(pluggedType, templateType).Distinct();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="pluggedType"></param>
    /// <param name="templateType"></param>
    /// <returns></returns>
    private static IEnumerable<Type> FindInterfacesThatClosesCore(Type pluggedType, Type templateType)
    {
        if (pluggedType == null) yield break;

        if (!pluggedType.IsConcrete()) yield break;

        if (templateType.IsInterface)
        {
            foreach (
                var interfaceType in
                pluggedType.GetInterfaces()
                    .Where(type => type.IsGenericType && (type.GetGenericTypeDefinition() == templateType)))
            {
                yield return interfaceType;
            }
        }
        else if (pluggedType.BaseType!.IsGenericType &&
                 (pluggedType.BaseType!.GetGenericTypeDefinition() == templateType))
        {
            yield return pluggedType.BaseType!;
        }

        if (pluggedType.BaseType == typeof(object)) yield break;

        foreach (var interfaceType in FindInterfacesThatClosesCore(pluggedType.BaseType!, templateType))
        {
            yield return interfaceType;
        }
    }

    /// <summary>
    /// 是否是实例类型
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private static bool IsConcrete(this Type type)
    {
        return !type.IsAbstract && !type.IsInterface;
    }


    private static void Fill<T>(this IList<T> list, T value)
    {
        if (list.Contains(value)) return;
        list.Add(value);
    }

    #endregion
}